#!/bin/bash
#
# build
#
# Build script for the Dragora GNU/Linux-Libre website
# (https://www.dragora.org)
#
#
# Copyright (C) 2020, 2021 Michael Siegel
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


#### INCLUDES ####

. include/constants || exit 1   
. include/subroutines || exit 1


#### CONSTANTS ####


#### GLOBAL VARIABLES ####

backpath=
build_testbed=0
css_path=
err_msg=  # Try to do without that. It's currently ununsed.
favicon_path=
lang=
logo_path=
mod_date=
page_title=
pg=
pg_basename_in=
pg_basename_out=
pg_path_in=
pg_path_out=
slashes=
sublevel=
title=


#### FUNCTIONS ####

_get_page_title() {
  ## Retrieve page title from input file
  
  sed -n '/^<!--PAGETITLE:.*-->$/ {
    s/<!--PAGETITLE://
    s/-->$//
    p
  }' "$1"
}

_get_regular_pages() {
  ## Find all HTML pages in "$PAGES_DIR"/"$lang", except those that belong to
  ## the Dragora Handbook

  find -- "$PAGES_DIR"/"$lang" -path "$PAGES_DIR"/"$lang"/doc/handbook -prune \
    -o -type f -name '*.html.in' -print
}

_mk_clean() {
  ## Remove page directories from $OUTPUT_DIR

  local dir=
  for dir in $(_get_lang_dirs)
  do
    rm -rf -- "${OUTPUT_DIR:?}/$dir" || \
      { _perr "cannot remove directory -- '$OUTPUT_DIR/$dir'"; return 1; }
  done
  unset dir
}

_mk_header() {
  ## Compile HTML page header

  printf '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"\n  "http://www.w3.org/TR/html4/strict.dtd">\n'
  printf '<html lang="%s">\n<head>\n' "$lang"
  printf \
    '%s<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\n' \
    "$IND1"
  printf '%s<link rel="stylesheet" type="text/css" href="%s">\n' "$IND1" \
    "$css_path"
  printf '%s<link rel="icon" type="image/gif" href="%s">\n' "$IND1" \
    "$favicon_path"
  printf '%s<title>%s</title>\n' "$IND1" "$title"
  printf '%s<meta name="description" content="%s">\n' "$IND1" \
    "$lang_description"
  printf '</head>\n<body>\n'
  printf '%s<div class="header" role="banner">\n' "$IND1"
  printf '%s<img src="%s" height="80" alt="Dragora logo">\n' "$IND2" \
    "$logo_path"
  printf '%s<div class="header_text">\n' "$IND2"
  printf '%s<h1>Dragora</h1>\n' "$IND3"
  printf '%s<p class="tagline">%s</p>\n' "$IND3" "$lang_tagline"
  printf '%s</div>  <!-- close header_text -->\n' "$IND2"
  printf '%s</div>  <!-- close header -->\n' "$IND1"
  printf '%s<div class="torso">\n' "$IND1"
}

_mk_indent() {
  ## Create an indentation string for a given level

  local ind=
  local level="$1"

  while [[ "$level" -gt 0 ]]
  do
    ind="${ind}${IND1}"
    ((--level))    
  done

  printf '%s' "$ind"
}

_pick_navitems() {
  # Print out only those items that are to be shown in the site navigation on
  # the current page

  local current_pg="${pg_path_out#*/}/"
  local depth_current_pg=
  local depth_item=
  local fc=
  local fi=
  local field=
  local ignore=
  local slashes=

  slashes="${current_pg//[^\/]}"
  depth_current_pg="${#slashes}"

  for item in "${navtree[@]}"
  do
    [[ "$build_testbed" -eq 0 && "$item" = testbed/* ]] && continue 

    slashes="${item//[^\/]}"
    depth_item="${#slashes}"

    # Ignore any item at a deeper nesting level than immediate sub-pages of
    # $current_pg.
    [[ "$depth_item" -gt "$((depth_current_pg + 1))" ]] && continue
    # The loop below can't catch those…

    ignore=0
    field=1
    while true
    do
     fc="$(cut -d / -f "$field" <<< "$current_pg")"
     fi="$(cut -d / -f "$field" <<< "$item")"

     [[ -n "$fi" && -n "$fc" ]] || break
     
     if [[ "$fi" != "$fc" ]]
     then
       [[ "$depth_item" -gt "$field" ]] && ignore=1 && break
     fi

     ((++field))
    done
    [[ "$ignore" -eq 1 ]] && continue

    printf '%s ' "$item"
  done
}

_mk_nav() {
  # Compile site navigation for the current page

  local current_pg="${pg_path_out#*/}/"
  local depth_curr=
  local depth_next=
  local ind=4
  local nav_label=
  local nav_path=
  local slashes=
  local sublists_opened=0

  printf '%s<div class="site_nav" role="navigation">\n' "$IND2"
  printf '%s<ul>\n' "$IND3"

  while [[ $# -gt 0 ]]
  do
    slashes="${1//[^\/]}"
    depth_curr="${#slashes}"
    slashes="${2//[^\/]}"
    depth_next="${#slashes}"

    nav_path="${backpath}${1}"
    nav_label="$(_get_page_title "${PAGES_DIR}/${lang}/${1}/index.html.in")"

    # Open list item:
    printf '%s<li>' "$(_mk_indent "$ind")"

    printf '<div class="site_nav_item'
    [[ "$1" = "$current_pg" ]] && printf ' selected'
    printf '"><a href="%s">%s</a></div>' "$nav_path" "$nav_label"

    # Next path has equal depth: Close list item: 
    if [[ "$depth_curr" -eq "$depth_next" ]]
    then
      printf '</li>\n'
    # Next path runs deeper: Open sublist for next nav item, increase count of
    # opened sublists:
    elif [[ "$depth_curr" -lt "$depth_next" ]]
    then
      ((++ind))
      printf '\n%s<ul>\n' "$(_mk_indent "$ind")"
      ((++sublists_opened))
      ((++ind))  # for <li> in the sublist
    # Next path runs less deep: Close list item, close appropriate number of
    # opened sublists, close surrounding list item.
    elif [[ "$depth_curr" -gt "$depth_next" ]]
    then
      printf '</li>\n'
      while [[ "$sublists_opened" -gt 0 && \
        "$((sublists_opened - depth_next))" -ge 0 ]]
      do
        ((--ind))
        printf '%s</ul>\n' "$(_mk_indent "$ind")"
        ((--sublists_opened))
        ((--ind))
        printf '%s</li>\n' "$(_mk_indent "$ind")"
      done
      # The loop condition explained:
      #   Part 1: Closing sublists runs up to and including
      #   $sublists_opened - $depth_next being 0. But if $2 is empty
      #   because we are already working on the final nav item, $depth_next
      #   will also be 0. Subtracting that from sublists_opened being 0 would,
      #   of course, itself be 0 and thus make the loop condition evaluate to
      #   true and result in printing an additonal sublist closing when all
      #   sublists have already been closed.
      #   Part 2: You can't just close all open sublists at this point because
      #   list items on lower (less deep) nesting levels might follow. So,
      #   closing opened sublists can only go as far as the nesting level of
      #   the next nav item.
    fi
    shift
  done

  printf '%s</ul>\n' "$IND3"
  printf '%s</div> <!-- close site_nav -->\n' "$IND2"
  printf '%s<div class="main" role="main">\n' "$IND2"
}

_mk_content() {
  ## Incorporate dynamic content modules, if any.

  local pg="$1"
  local lang="$2"
  local has_modules=1
#  local tmpfile_1=
#  local tmpfile_2=
#
#  tmpfile_1="$(mktemp "$TMP_DIR"/XXXXXX)"
#  tmpfile_2="$(mktemp "$TMP_DIR"/XXXXXX)"
#
#  Two temporary files will be needed for copying content back an forth, as
#  soon as there is more than one dynamic content module.

  if grep -q '^<!--LATEST_NEWS-->$' "$pg"
  then
    has_modules=0
    sed '1,/^<!--LATEST_NEWS-->$/ !d' "$pg" && \
      grep -A "$((LATEST_NEWS_ITEMS * 3))" -- '^<ul>$' \
      "${PAGES_DIR}/${lang}/$NEWS_PAGE" | sed '$ a <\/ul>' && \
      sed '1,/^<!--LATEST_NEWS-->$/ d' "$pg"
  fi

  [[ "$has_modules" -eq 1 ]] && cat "$pg"
}

_mk_footer() {
  ## Compile HTML page footer

  printf '\n<p class="back_top"><a href="#">%s</a></p>\n\n' "$lang_back_to_top"
  printf '%s</div> <!-- close main -->\n' "$IND2"
  printf '%s</div> <!-- close torso -->\n' "$IND1"
  printf '%s<hr class="footer_hr">\n' "$IND1"
  printf '%s<div class="footer" role="contentinfo">\n' "$IND1"
  printf '%s<p>%s: %s</p>\n' "$IND2" "$lang_mod_label" "$mod_date"
  printf '%s<p>Copyright © %s %s<br>\n' "$IND2" "$COPYRIGHT_YEARS" \
    "$COPYRIGHT_HOLDERS"
  printf '%s<a href="%slicense/">%s</a>\n' "$IND3" "${backpath}" \
    "$(_get_page_title "${PAGES_DIR}/${lang}/license/index.html.in")"
  printf '%s</p>\n' "$IND2"
  printf '%s</div> <!-- close footer -->\n' "$IND1"
  printf '</body>\n</html>\n'
}

_mk_pretty() {
  ## Remove meta comments from page source

  sed -e '/^<!--PAGETITLE:.*-->$/ d' \
      -e '/^<!--LATEST_NEWS-->$/ d' <<< "$1"
}

_set_backpath() {

  backpath=  # If you don't, the string will grow with every invocation.

  while [[ "$sublevel" -gt 0 ]]
  do
    backpath="${backpath}$DIR_UP"
    ((--sublevel))
  done
}


#### MAIN ####

# TODO: implement traps


## Environment checks

_env_checks || _abort

## Parse command-line arguments

[[ "$1" = '-t' ]] && build_testbed=1


## Remove page directories from $OUTPUT_DIR, then rsync static files
## Order matters!

_mk_clean || _abort
rsync -av --delete "$COMMON_DIR"/ "$OUTPUT_DIR" || _abort


# Compile pages

_get_navtree

for lang in $(_get_lang_dirs)
do
  # Get values for language-specific parameters in header and footer

  . "$PAGES_DIR/$lang/$HEADER_PARAMS" || _abort
  . "$PAGES_DIR/$lang/$FOOTER_PARAMS" || _abort


  # Compile pages

  for pg in $(_get_regular_pages)
  do
    # Set path and file names

    pg_path_in="${pg%/*}"
    pg_path_out="$(sed "s,$PAGES_DIR/,," <<< "$pg_path_in")"
#    echo "$pg_path_out" # DEBUG
    pg_basename_in="${pg##*/}"
    pg_basename_out="${pg_basename_in%.in}"  # .html.in → .html

    if [[ "$build_testbed" -eq 0 ]]
    then
      # Skip testbed pages
      [[ "$(cut -d '/' -f 2 <<< "$pg_path_out")" = testbed ]] && continue
    fi

    slashes="${pg_path_out//[^\/]}"
    sublevel="${#slashes}"

    _set_backpath  # makes use of $sublevel

    # these should, maybe, be 'local' to _mk_header
    css_path="${DIR_UP}${backpath}${STYLESHEET_PATH}"
    favicon_path="${DIR_UP}${backpath}${FAVICON_PATH}"
    logo_path="${DIR_UP}${backpath}${LOGO_PATH}"

    # Determine page title

    page_title="$(_get_page_title "$pg")"
    title="$SITE_TITLE $SEPARATOR $page_title"

    # Determine modification date (requires GNU version of `stat')

    mod_date="$(TZ="$TIMEZONE" stat -c '%y' -- "$pg" | cut -d '.' -f 1)"
    mod_date="$mod_date $TIMEZONE"

    # Action

    mkdir -p -- "$OUTPUT_DIR"/"$pg_path_out" || _abort

    { _mk_header && _mk_nav $(_pick_navitems) && \
      _mk_pretty "$(_mk_content "$pg" "$lang")" && _mk_footer; } \
      > "$OUTPUT_DIR"/"$pg_path_out"/"$pg_basename_out" || _abort
  done
  ln -s -- "$HOME_PAGE_DIR"/index.html "$OUTPUT_DIR"/"$lang"/index.html
done
  ln -s -- "${PAGES_DIR_MASTER##*/}"/"$HOME_PAGE_DIR"/index.html \
    "$OUTPUT_DIR"/index.html
_cleanup
